//
// 嗨~ o(*￣▽￣*)ブ
// Created by Mokou on 2020/7/16.
//

#import "FileCopy.h"
#import "Util.h"

#define defaultCopyLength 1024*1024*10

@implementation FileCopy {

}

- (instancetype)init {
    if (self = [super init]) {
        self.rate = 0.00f;
        self.copyLength = defaultCopyLength;
    }
    return self;
}

- (instancetype)initWithCopyLength:(NSUInteger)length {
    if (self = [super init]) {
        self.rate = 0.00f;
        self.copyLength = length < (1024 * 1024) ? defaultCopyLength : length;
    }
    return self;
}

- (BOOL)copyFile:(NSString *)targetFile data:(NSData *)data {
    NSFileManager *fm = [NSFileManager defaultManager];
    if (![fm fileExistsAtPath:targetFile]) {
        BOOL y = [fm createFileAtPath:targetFile contents:nil attributes:nil];
        if (!y) {
            //create targetFile error
            return NO;
        }
    }
    return [data writeToFile:targetFile atomically:YES];
}

- (BOOL)copyFile:(NSString *)sourceFile toFilePath:(NSString *)targetFile {
    if (@available(iOS 13.0, *)) {
        return [self copyFile_new:sourceFile toFilePath:targetFile];
    } else {
        return [self copyFile_old:sourceFile toFilePath:targetFile];
    }
}

- (BOOL)copyFile_new:(NSString *)sourceFile toFilePath:(NSString *)targetFile {
    if (!@available(iOS 13.0, *)) return NO;

    NSFileManager *fm = [NSFileManager defaultManager];
    if (![fm fileExistsAtPath:sourceFile]) {
        //sourceFile is not existed! Please checked first
        return NO;
    }
    if (![fm fileExistsAtPath:targetFile]) {
        BOOL y = [fm createFileAtPath:targetFile contents:nil attributes:nil];
        if (!y) {
            //create targetFile error
            return NO;
        }
    }

    @try {
        NSDictionary *sourceFileInfo = [fm attributesOfItemAtPath:sourceFile error:nil];
        NSNumber *sourceFileLength = sourceFileInfo[NSFileSize];

        NSFileHandle *sourceHandle = [NSFileHandle fileHandleForReadingAtPath:sourceFile];
        NSFileHandle *targetHandle = [NSFileHandle fileHandleForWritingAtPath:targetFile];
        NSError *err;
        BOOL success = [targetHandle truncateAtOffset:0 error:&err];
        if (!success) {
            //truncate target error
            return NO;
        }

        BOOL copyFlag = YES;
        unsigned long long int totalLength = [sourceFileLength unsignedLongLongValue];
        unsigned long long int readLength = 0;
        while (copyFlag) {
            NSData *tmpData = nil;
            unsigned long long int l = totalLength - readLength;

            NSError *readErr;
            NSError *seekErr;
            NSError *writeErr;
            if (l < self.copyLength) {
                tmpData = [sourceHandle readDataToEndOfFileAndReturnError:&readErr];
                readLength = totalLength;
                copyFlag = NO;
            } else {
                tmpData = [sourceHandle readDataUpToLength:(NSUInteger) self.copyLength error:&readErr];
                readLength += self.copyLength;
                [sourceHandle seekToOffset:readLength error:&seekErr];
            }

            if (![targetHandle seekToEndReturningOffset:nil error:&seekErr]) {
                return NO;
            }

            if (![targetHandle writeData:tmpData error:&writeErr]) {
                return NO;
            }

            [self setRate:readLength total:totalLength];
        }

        NSError *sourceErr;
        NSError *targetErr;
        if (![sourceHandle closeAndReturnError:&sourceErr]) {
            return NO;
        }
        if (![targetHandle closeAndReturnError:&targetErr]) {
            return NO;
        }
    }
    @catch (NSException *exception) {
        NSString *err = [NSString stringWithFormat:@"error:%@", [exception reason]];
        return NO;
    }
    @finally {}

    return YES;
}

- (BOOL)copyFile_old:(NSString *)sourceFile toFilePath:(NSString *)targetFile {
    if (@available(iOS 13.0, *)) return NO;

    NSFileManager *fm = [NSFileManager defaultManager];
    if (![fm fileExistsAtPath:sourceFile]) {
        return NO;
    }
    if (![fm fileExistsAtPath:targetFile]) {
        BOOL y = [fm createFileAtPath:targetFile contents:nil attributes:nil];
        if (!y) {
            return NO;
        }
    }

    @try {
        NSDictionary *sourceFileInfo = [fm attributesOfItemAtPath:sourceFile error:nil];
        NSNumber *sourceFileLength = sourceFileInfo[NSFileSize];

        NSFileHandle *sourceHandle = [NSFileHandle fileHandleForReadingAtPath:sourceFile];
        NSFileHandle *targetHandle = [NSFileHandle fileHandleForWritingAtPath:targetFile];
        [targetHandle truncateFileAtOffset:0];//truncate targetfile for init it

        BOOL copyFlag = YES;
        unsigned long long int totalLength = [sourceFileLength unsignedLongLongValue];
        unsigned long long int readLength = 0;
        while (copyFlag) {
            NSData *tmpData = nil;
            unsigned long long int l = totalLength - readLength;

            if (l < self.copyLength) {
                tmpData = [sourceHandle readDataToEndOfFile];
                readLength = totalLength;
                copyFlag = NO;
            } else {
                tmpData = [sourceHandle readDataOfLength:self.copyLength];
                readLength += self.copyLength;
                [sourceHandle seekToFileOffset:readLength];
            }
            [targetHandle seekToEndOfFile];
            [targetHandle writeData:tmpData];

        }

        [sourceHandle closeFile];
        [sourceHandle closeFile];

    }
    @catch (NSException *exception) {
        NSString *err = [NSString stringWithFormat:@"error:%@", [exception reason]];
        return NO;
    }
    @finally {}

    return YES;
}

- (void)setRate:(unsigned long long int)a total:(unsigned long long int)b {
    self.rate = (float)a / (float)b;
}

@end
